// <copyright file="DungeonGenerator.cs" company="TenneySoftware">
// Copyright (c) 2009 All Right Reserved
// </copyright>
// <author>Robert Tenney</author>
// <email>rob10e@yahoo.com</email>
// <date>April 2009</date>
// <summary>Dungeon level generator.</summary>

// License:

// Product: RPGRoguelikeEngine .Net
// Author: Robert N. Tenney
// Copyright: April 2009

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation version 2
// of the License.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

// This license covers all source code, binaries, and other materials related to this project
// unless specifically stated otherwise.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Windows.Forms;

using TenneySoftware.Engine;

namespace TenneySoftware.LevelGenerator.DungeonGenerator {
    /// <summary>
    /// Dungeon Generation alorithm that uses weights as well as many different customizing
    /// options.
    /// </summary>
    public partial class Dungeon : ILevelGenerator {
        // TODO: Define room types.
        // FIXME: Try another wandering hall algorithm.
        // TODO: Optimize, optimize, optimize.......Code is too sluggish

        /// <summary>
        /// TileData array of the level.
        /// </summary>
        private Level level;

        /// <summary>
        /// Random number generator.
        /// </summary>
        private Random rand;

        /// <summary>
        /// Seed for use with the random number generator.
        /// </summary>
        private int seed;

        /// <summary>
        /// Maximum size for rooms.
        /// </summary>
        private int maxRoomSize;

        /// <summary>
        /// Minimum size for rooms.
        /// </summary>
        private int minRoomSize;

        /// <summary>
        /// List of preferred connectors.
        /// </summary>
        private List<Heading> preferredConnectors;

        /// <summary>
        /// Maximum times a placement can fail before it gives up on placement.
        /// </summary>
        private int failure;

        /// <summary>
        /// An arraylist of hall tiles placed.
        /// </summary>
        private ArrayList hallSegments;

        /// <summary>
        /// A list of rooms placed.
        /// </summary>
        private List<RoomData> roomList;

        /// <summary>
        /// Global: Intersection point in hallway generation.
        /// </summary>
        private IntersectionPoint intersection;

        /// <summary>
        /// Global: Flag to indicate if a structure placement came from a room or not.
        /// </summary>
        private bool fromRoom;

        /// <summary>
        /// Global: Flag to indicate if a structure placement leads to a room or not.
        /// </summary>
        private bool toRoom;

        /// <summary>
        /// A hashset list of doors placed.
        /// </summary>
        private HashSet<Point> doorList;

        /// <summary>
        /// Cavern generator to use for placing caves within the level.
        /// </summary>
        private ILevelGenerator cave;

        /// <summary>
        /// Assembly pointing to the cavern generation module.
        /// </summary>
        private Assembly cavernAssem;

        /// <summary>
        /// Dictionary of structure weights.
        /// </summary>
        private Dictionary<StructureType, double> structureWeights;

        /// <summary>
        /// Preference for placing rooms rather than hallways.
        /// </summary>
        private int roomBias;

        /// <summary>
        /// Likeliness to use preferred connectors over finding a random point to build from.
        /// </summary>
        private int preferredConnectorsChance;

        /// <summary>
        /// Flag to indicate whether dead ends should be removed.
        /// </summary>
        private bool removeDeadEnds;

        /// <summary>
        /// Flag to indicate whether or not wandering halls can intersect with other hall segments.
        /// </summary>
        private bool intersectWanderingHalls;

        /// <summary>
        /// Density of structure placement.
        /// </summary>
        private int density;

        /// <summary>
        /// Name of the predefined settings being used.
        /// </summary>
        private string settingName;

        /// <summary>
        /// Initializes a new instance of the Dungeon class.
        /// </summary>
        public Dungeon() : this(0) {
        }

        /// <summary>
        /// Initializes a new instance of the Dungeon class.
        /// </summary>
        /// <param name="size">Size to set the level to.</param>
        public Dungeon(Size size) : this(0, size) {
        }

        /// <summary>
        /// Initializes a new instance of the Dungeon class.
        /// </summary>
        /// <param name="predefined">A predefined code to use.</param>
        public Dungeon(int predefined) : this(predefined, new Size(64, 64)) {
        }

        /// <summary>
        /// Initializes a new instance of the Dungeon class.
        /// </summary>
        /// <param name="predefined">Integer representation of predefine.</param>
        /// <param name="size">Size to make level.</param>
        public Dungeon(int predefined, Size size) {
            this.structureWeights = new Dictionary<StructureType, double>();
            this.UsePredefinedSettings(predefined);
            this.SetSize(size);
            this.preferredConnectors = new List<Heading>();
            this.roomList = new List<RoomData>();
            this.hallSegments = new ArrayList();
            this.intersection = new IntersectionPoint();
            this.doorList = new HashSet<Point>();
            string dir;
            dir = Application.ExecutablePath;
            string file = Path.Combine(Path.Combine(Path.GetDirectoryName(dir), "LevelGenerators"), "Cavern.dll");
            this.cavernAssem = Assembly.LoadFile(file);
        }

        /// <summary>
        /// A static method that will return an array of integers as a generated level. This
        /// method uses the PredefinedSettings_Simple and bypasses instatiation of the
        /// DungeonGenerator as well as all of it's advanced features. Use this method if
        /// you only want a simple level generator and will define your own level manager.
        /// </summary>
        /// <param name="size">Size to generate the level.</param>
        /// <returns>An array of integers that represent the results of the generator. 0 =
        /// wall, 1 = floor, 2 = door.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1814:PreferJaggedArraysOverMultidimensional", MessageId = "Return", Justification = "Multidimentional preferred")]
        public static int[,] SimpleDungeon(Size size) {
            Dungeon temp = new Dungeon(3, size);
            temp.Generate();
            return temp.LevelArray();
        }

        /// <summary>
        /// Returns a string representation of the Dungeon Generator.
        /// </summary>
        /// <returns>A string representation of this class.</returns>
        public override string ToString() {
            return this.ModuleName + " | " + this.SettingsName;
        }

        /// <summary>
        /// A structure to hold data related to the results of processing the StructureWeights.
        /// </summary>
        private struct WeightedResults {
            /// <summary>
            /// A flag for whether the result of processing is for a room or for a hall.
            /// </summary>
            public bool IsRoom;

            /// <summary>
            /// A flag for whether the result of processing is for a round room or a regular room.
            /// </summary>
            public bool IsRound;

            /// <summary>
            /// A flag for whether the result of processing is for a cave or not.
            /// </summary>
            public bool IsCave;

            /// <summary>
            /// The size from the result of processing for rooms and caves.
            /// </summary>
            public Size RoomCaveSize;

            /// <summary>
            /// The length from the result of processing for hallways.
            /// </summary>
            public int HallLength;

            /// <summary>
            /// A flag for whether the result of processing is for a wandering hallway or not.
            /// </summary>
            public bool IsWander;

            /// <summary>
            /// Type of structure.
            /// </summary>
            public StructureType Type;
        }

        /// <summary>
        /// A structure to hold data related to the point of intersection for wandering halls.
        /// </summary>
        private struct IntersectionPoint {
            /// <summary>
            /// The point where intersection occured.
            /// </summary>
            public Point Location;

            /// <summary>
            /// A flag to indicate whether the intersection was caused by a room, or hallway.
            /// </summary>
            public bool ConnectedToRoom;
        }

        /// <summary>
        /// A structure to hold data related to room construction.
        /// </summary>
        private struct RoomData {
            /// <summary>
            /// A flag to indicate whether the room is round or not.
            /// </summary>
            public bool IsRound;

            /// <summary>
            /// The Rectagular region where the room is located.
            /// </summary>
            public Rectangle Area;

            /// <summary>
            /// Type of room.
            /// </summary>
            public StructureType Type;

            /// <summary>
            /// Initializes a new instance of the RoomData struct.
            /// </summary>
            /// <param name="area">Area of room.</param>
            /// <param name="isRound">Is the room round or not.</param>
            /// <param name="type">Type of room.</param>
            public RoomData(Rectangle area, bool isRound, StructureType type) {
                this.IsRound = isRound;
                this.Area = area;
                this.Type = type;
            }
        }

        /// <summary>
        /// A class to hold data related to a location and direction.
        /// </summary>
        private class Heading {
            /// <summary>
            /// Location of heading.
            /// </summary>
            public Point Location;

            /// <summary>
            /// Direction of heading.
            /// </summary>
            public Direction Direction;

            /// <summary>
            /// Initializes a new instance of the Heading class.
            /// </summary>
            /// <param name="location">Location for heading.</param>
            /// <param name="direction">Direction of heading.</param>
            public Heading(Point location, Direction direction) {
                this.Location = location;
                this.Direction = direction;
            }

            /*/// <summary>
            /// Gets or sets the location of the heading.
            /// </summary>
            /// <value>
            /// The location of the heading.
            /// </value>
            public Point Location {
                get {
                    return this.location;
                }

                set {
                    this.location = value;
                }
            }

            /// <summary>
            /// Gets or sets the direction of the heading.
            /// </summary>
            /// <value>
            /// The direction of the heading.
            /// </value>
            public Direction Direction {
                get {
                    return this.direction;
                }

                set {
                    this.direction = value;
                }
            }*/

            /// <summary>
            /// Converts this class into a string.
            /// </summary>
            /// <returns>Returns a string representation of this class.</returns>
            public override string ToString() {
                return this.Location.ToString() + " - " + this.Direction.ToString();
            }
        }
    }
}
